/**
 * 
 */
package engine.operators;

import its.opt.structures.BOAParams;
import its.util.RandomFactory;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import engine.BoaPopulation;
import engine.ObjectiveFunction;
import engine.Operator;
import engine.Population;
import engine.individuals.ProgramedTrafficNetIndividual;
import engine.individuals.util.IndividualsComparator;

/**
 * @author Rafal Hladyszowski
 * 
 */
public class RestrictedTournamentSelectionOperator
implements Operator<ProgramedTrafficNetIndividual> {

	private int numberOfCompetitors;

	private ObjectiveFunction<ProgramedTrafficNetIndividual> objectiveFunction;

	private Comparator<ProgramedTrafficNetIndividual> individualsComparator;



	public RestrictedTournamentSelectionOperator(
			ObjectiveFunction<ProgramedTrafficNetIndividual> objectiveFunction, 
			BOAParams params)
	{
		this.numberOfCompetitors = params.tournamentSize;
		this.objectiveFunction = objectiveFunction;
		this.individualsComparator = new IndividualsComparator<ProgramedTrafficNetIndividual>(objectiveFunction);
	}

	/*
	 * (non-Javadoc)
	 * @see engine.BoaOperator#apply(engine.BoaPopulation, java.util.List)
	 */
	public BoaPopulation<ProgramedTrafficNetIndividual> apply(BoaPopulation<ProgramedTrafficNetIndividual> population,
			List<Population<ProgramedTrafficNetIndividual>> populations) {

		Population<ProgramedTrafficNetIndividual> originalPopulation = populations.get(0);
		int originalPopulationSize = originalPopulation.size();
		int offspringPopulationSize = population.size();
		int chromosomeSize = population.getChromosomeSize();

		int similarpos = 0;

//		boolean bin = false;

		List<ProgramedTrafficNetIndividual> replacedIndividuals = new ArrayList<ProgramedTrafficNetIndividual>(
				originalPopulation.getIndividuals());

		for (int j = 0; j < offspringPopulationSize; j++) {
			int mindistance = Integer.MAX_VALUE;
			for (int i = 0; i < numberOfCompetitors; i++) {
				int pos = RandomFactory.getNextInt(originalPopulationSize);
				int dist = individualsDistance(population.getIndividuals()
						.get(j), originalPopulation.getIndividuals().get(pos));

				if (dist < mindistance) {
					mindistance = dist;
					similarpos = pos;
					if (dist == 0.0)
						break;
				}
			}
			if (objectiveFunction.compute(population.getIndividuals().get(j)) > objectiveFunction
					.compute(originalPopulation.getIndividuals().get(similarpos))) {
				replacedIndividuals.set(similarpos, population
						.getIndividuals().get(j));
			}
		}

		BoaPopulation<ProgramedTrafficNetIndividual> replacedPopulation = new BoaPopulation<ProgramedTrafficNetIndividual>(
				replacedIndividuals, chromosomeSize);

		return replacedPopulation;
	}

	private int individualsDistance(
			ProgramedTrafficNetIndividual individual1,
			ProgramedTrafficNetIndividual individual2)
	{
		int dist = 0;
//	  float fdist = 0.0f;

	  int programsSize = individual1.getProgramsSize();
	  
	  for (int i=0; i<programsSize; i++) {
//	    switch (problems[activeProblem].geneType(i)) {
//	   case BINARY:
//	  	 dist += individual1.get(i) ^ individual2.get(i);
//	     break;
//	   case CONTINUOUS: 
	  	 dist += Math.abs(individual1.getProgramVariableAt(i) - individual2.getProgramVariableAt(i));
//	     break;
//	   case CATEGORICAL: if (chr1[i].cat != chr2[i].cat) dist ++;
//	     break;
//	    }
	  }
	  return dist/*+fdist*/;
	}

	@Override
	public Population<ProgramedTrafficNetIndividual> apply(Population<ProgramedTrafficNetIndividual> population)
	{
		int N = population.size();

		Population<ProgramedTrafficNetIndividual> parents = new Population<ProgramedTrafficNetIndividual>(new ArrayList<ProgramedTrafficNetIndividual>(N));

		List<ProgramedTrafficNetIndividual> individuals = population.getIndividuals();

		for (int j = 0; j < N; j++) {
			int mindistance = Integer.MAX_VALUE;
			int picked = RandomFactory.getNextInt(N);
			ProgramedTrafficNetIndividual individual = individuals.get(picked);
			ProgramedTrafficNetIndividual closest = individual;
			for (int i = 0; i < numberOfCompetitors; i++)
			{
				picked = RandomFactory.getNextInt(N);
				ProgramedTrafficNetIndividual pairedIndividual = individuals.get(picked);
				int dist = individualsDistance(individual, pairedIndividual);

				if (dist < mindistance) {
					mindistance = dist;
					closest = pairedIndividual;
					if (dist == 0.0)
						break;
				}
			}
			if (individualsComparator.compare(individual, closest) > 0) {
				parents.addIndividual(individual);
			} else {
				parents.addIndividual(closest);
			}
		}
		
		return parents;
	}

}
